#include "Dump.h"
#include "../IStreamCapture.h"
extern "C"
{
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libavutil/replaygain.h>
#include <libavutil/stereo3d.h>
#include <libavutil/mastering_display_metadata.h>
#include <libavutil/spherical.h>
}

#define GLOBAL_BUFFER_SIZE 1024
char globalBuffer[GLOBAL_BUFFER_SIZE];

char* AssemblyBuffer(char const* const _Format, ...)
{
	va_list ap;
	va_start(ap, _Format);
	memset(globalBuffer, 0, GLOBAL_BUFFER_SIZE);
	vsprintf(globalBuffer, _Format, ap);
	va_end(ap);
	return globalBuffer;
}
size_t av_strlcpy(char *dst, const char *src, size_t size)
{
	size_t len = 0;
	while (++len < size && *src)
		*dst++ = *src++;
	if (len <= size)
		*dst = 0;
	return len + strlen(src) - 1;
}

static std::string dump_metadata(void *ctx, AVDictionary *m, const char *indent)
{
	std::string retstr = "";
	if (m && !(av_dict_count(m) == 1 && av_dict_get(m, "language", NULL, 0))) {
		AVDictionaryEntry *tag = NULL;

		retstr += AssemblyBuffer("%sMetadata:\n", indent);
		av_log(ctx, AV_LOG_INFO, "%sMetadata:\n", indent);
		while ((tag = av_dict_get(m, "", tag, AV_DICT_IGNORE_SUFFIX)))
			if (strcmp("language", tag->key)) {
				const char *p = tag->value;
				retstr += AssemblyBuffer("%s  %-16s: ", indent, tag->key);
				av_log(ctx, AV_LOG_INFO,
					"%s  %-16s: ", indent, tag->key);
				while (*p) {
					char tmp[256];
					size_t len = strcspn(p, "\x8\xa\xb\xc\xd");
					av_strlcpy(tmp, p, FFMIN(sizeof(tmp), len + 1));
					retstr += AssemblyBuffer("%s", tmp);
					av_log(ctx, AV_LOG_INFO, "%s", tmp);
					p += len;
					if (*p == 0xd)
					{
						retstr += AssemblyBuffer(" ");
						av_log(ctx, AV_LOG_INFO, " ");
					}
					if (*p == 0xa)
					{
						retstr += AssemblyBuffer("\n%s  %-16s: ", indent, "");
						av_log(ctx, AV_LOG_INFO, "\n%s  %-16s: ", indent, "");
					}
						
					if (*p) p++;
				}
				retstr += AssemblyBuffer("\n");
				av_log(ctx, AV_LOG_INFO, "\n");
			}
	}
	return retstr;
}

static std::string print_fps(double d, const char *postfix)
{
	uint64_t v = lrintf(d * 100);
	std::string retstr = "";
	if (!v)
	{
		retstr += AssemblyBuffer("%1.4f %s", d, postfix);
		av_log(NULL, AV_LOG_INFO, "%1.4f %s", d, postfix);
	}
	else if (v % 100)
	{
		retstr += AssemblyBuffer("%3.2f %s", d, postfix);
		av_log(NULL, AV_LOG_INFO, "%3.2f %s", d, postfix);
	}
	else if (v % (100 * 1000))
	{
		retstr += AssemblyBuffer("%1.0f %s", d, postfix);
		av_log(NULL, AV_LOG_INFO, "%1.0f %s", d, postfix);
	}
	else
	{
		retstr += AssemblyBuffer("%1.0fk %s", d / 1000, postfix);
		av_log(NULL, AV_LOG_INFO, "%1.0fk %s", d / 1000, postfix);
	}
	return retstr;
}

#ifndef AV_RL32
#   define AV_RL32(x)                                \
    (((uint32_t)((const uint8_t*)(x))[3] << 24) |    \
               (((const uint8_t*)(x))[2] << 16) |    \
               (((const uint8_t*)(x))[1] <<  8) |    \
                ((const uint8_t*)(x))[0])
#endif
#ifndef AV_RL64
#   define AV_RL64(x)                                   \
    (((uint64_t)((const uint8_t*)(x))[7] << 56) |       \
     ((uint64_t)((const uint8_t*)(x))[6] << 48) |       \
     ((uint64_t)((const uint8_t*)(x))[5] << 40) |       \
     ((uint64_t)((const uint8_t*)(x))[4] << 32) |       \
     ((uint64_t)((const uint8_t*)(x))[3] << 24) |       \
     ((uint64_t)((const uint8_t*)(x))[2] << 16) |       \
     ((uint64_t)((const uint8_t*)(x))[1] <<  8) |       \
      (uint64_t)((const uint8_t*)(x))[0])
#endif
/* param change side data*/
static std::string dump_paramchange(void *ctx, AVPacketSideData *sd)
{
	std::string retstr = "";
	int size = sd->size;
	const uint8_t *data = sd->data;
	uint32_t flags, channels, sample_rate, width, height;
	uint64_t layout;

	if (!data || sd->size < 4)
		goto fail;

	flags = AV_RL32(data);
	data += 4;
	size -= 4;

	if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_COUNT) {
		if (size < 4)
			goto fail;
		channels = AV_RL32(data);
		data += 4;
		size -= 4;
		retstr += AssemblyBuffer("channel count %u, ", channels);
		av_log(ctx, AV_LOG_INFO, "channel count %u, ", channels);
	}
	if (flags & AV_SIDE_DATA_PARAM_CHANGE_CHANNEL_LAYOUT) {
		if (size < 8)
			goto fail;
		layout = AV_RL64(data);
		data += 8;
		size -= 8;
		retstr += AssemblyBuffer("channel layout: %s, ", av_get_channel_name(layout));
		av_log(ctx, AV_LOG_INFO,
			"channel layout: %s, ", av_get_channel_name(layout));
	}
	if (flags & AV_SIDE_DATA_PARAM_CHANGE_SAMPLE_RATE) {
		if (size < 4)
			goto fail;
		sample_rate = AV_RL32(data);
		data += 4;
		size -= 4;
		retstr += AssemblyBuffer("sample_rate %u, ", sample_rate);
		av_log(ctx, AV_LOG_INFO, "sample_rate %u, ", sample_rate);
	}
	if (flags & AV_SIDE_DATA_PARAM_CHANGE_DIMENSIONS) {
		if (size < 8)
			goto fail;
		width = AV_RL32(data);
		data += 4;
		size -= 4;
		height = AV_RL32(data);
		data += 4;
		size -= 4;
		retstr += AssemblyBuffer("width %u height %u", width, height);
		av_log(ctx, AV_LOG_INFO, "width %u height %u", width, height);
	}

	return retstr;
fail:
	retstr += AssemblyBuffer("unknown param");
	av_log(ctx, AV_LOG_INFO, "unknown param");
	return retstr;
}
/* replaygain side data*/
static std::string print_gain(void *ctx, const char *str, int32_t gain)
{
	std::string retstr = "";
	retstr += AssemblyBuffer("%s - ", str);
	av_log(ctx, AV_LOG_INFO, "%s - ", str);
	if (gain == INT32_MIN)
	{
		retstr += AssemblyBuffer("unknown");
		av_log(ctx, AV_LOG_INFO, "unknown");
	}
	else
	{
		retstr += AssemblyBuffer("%f", gain / 100000.0f);
		av_log(ctx, AV_LOG_INFO, "%f", gain / 100000.0f);
	}
	retstr += AssemblyBuffer(", ");
	av_log(ctx, AV_LOG_INFO, ", ");
	return retstr;
}
static std::string print_peak(void *ctx, const char *str, uint32_t peak)
{
	std::string retstr = "";
	retstr += AssemblyBuffer("%s - ", str);
	av_log(ctx, AV_LOG_INFO, "%s - ", str);
	if (!peak)
	{
		retstr += AssemblyBuffer("unknown");
		av_log(ctx, AV_LOG_INFO, "unknown");
	}
	else
	{
		retstr += AssemblyBuffer("%f", (float)peak / UINT32_MAX);
		av_log(ctx, AV_LOG_INFO, "%f", (float)peak / UINT32_MAX);
	}
	retstr += AssemblyBuffer(", ");
	av_log(ctx, AV_LOG_INFO, ", ");
	return retstr;
}
static std::string dump_replaygain(void *ctx, AVPacketSideData *sd)
{
	std::string retstr = "";
	AVReplayGain *rg;

	if (sd->size < sizeof(*rg)) {
		retstr += AssemblyBuffer("invalid data");
		av_log(ctx, AV_LOG_INFO, "invalid data");
		return retstr;
	}
	rg = (AVReplayGain*)sd->data;

	retstr += print_gain(ctx, "track gain", rg->track_gain);
	retstr += print_peak(ctx, "track peak", rg->track_peak);
	retstr += print_gain(ctx, "album gain", rg->album_gain);
	retstr += print_peak(ctx, "album peak", rg->album_peak);
	return retstr;
}

static std::string dump_stereo3d(void *ctx, AVPacketSideData *sd)
{
	std::string retstr = "";
	AVStereo3D *stereo;

	if (sd->size < sizeof(*stereo)) {
		retstr += AssemblyBuffer("invalid data");
		av_log(ctx, AV_LOG_INFO, "invalid data");
		return retstr;
	}

	stereo = (AVStereo3D *)sd->data;

	retstr += AssemblyBuffer("%s", av_stereo3d_type_name(stereo->type));
	av_log(ctx, AV_LOG_INFO, "%s", av_stereo3d_type_name(stereo->type));

	if (stereo->flags & AV_STEREO3D_FLAG_INVERT)
	{
		retstr += AssemblyBuffer(" (inverted)");
		av_log(ctx, AV_LOG_INFO, " (inverted)");
	}
	return retstr;
}

static std::string dump_audioservicetype(void *ctx, AVPacketSideData *sd)
{
	std::string retstr = "";
	enum AVAudioServiceType *ast = (enum AVAudioServiceType *)sd->data;

	if (sd->size < sizeof(*ast)) {
		retstr += AssemblyBuffer("invalid data");
		av_log(ctx, AV_LOG_INFO, "invalid data");
		return retstr;
	}

	switch (*ast) {
	case AV_AUDIO_SERVICE_TYPE_MAIN:
		retstr += AssemblyBuffer("main");
		av_log(ctx, AV_LOG_INFO, "main");
		break;
	case AV_AUDIO_SERVICE_TYPE_EFFECTS:
		retstr += AssemblyBuffer("effects");
		av_log(ctx, AV_LOG_INFO, "effects");
		break;
	case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
		retstr += AssemblyBuffer("visually impaired");
		av_log(ctx, AV_LOG_INFO, "visually impaired");
		break;
	case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
		retstr += AssemblyBuffer("hearing impaired");
		av_log(ctx, AV_LOG_INFO, "hearing impaired");
		break;
	case AV_AUDIO_SERVICE_TYPE_DIALOGUE:
		retstr += AssemblyBuffer("dialogue");
		av_log(ctx, AV_LOG_INFO, "dialogue");
		break;
	case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
		retstr += AssemblyBuffer("commentary");
		av_log(ctx, AV_LOG_INFO, "commentary");
		break;
	case AV_AUDIO_SERVICE_TYPE_EMERGENCY:
		retstr += AssemblyBuffer("emergency");
		av_log(ctx, AV_LOG_INFO, "emergency");
		break;
	case AV_AUDIO_SERVICE_TYPE_VOICE_OVER:
		retstr += AssemblyBuffer("voice over");
		av_log(ctx, AV_LOG_INFO, "voice over");
		break;
	case AV_AUDIO_SERVICE_TYPE_KARAOKE:
		retstr += AssemblyBuffer("karaoke");
		av_log(ctx, AV_LOG_INFO, "karaoke");
		break;
	default:
		retstr += AssemblyBuffer("unknown");
		av_log(ctx, AV_LOG_WARNING, "unknown");
		break;
	}
	return retstr;
}

static std::string dump_cpb(void *ctx, AVPacketSideData *sd)
{
	std::string retstr = "";
	AVCPBProperties *cpb = (AVCPBProperties *)sd->data;

	if (sd->size < sizeof(*cpb)) {
		retstr += AssemblyBuffer("invalid data");
		av_log(ctx, AV_LOG_INFO, "invalid data");
		return retstr;
	}
	retstr += AssemblyBuffer("bitrate max/min/avg: %d/%d/%d buffer size: %d vbv_delay: %lld",
		cpb->max_bitrate, cpb->min_bitrate, cpb->avg_bitrate,
		cpb->buffer_size,
		cpb->vbv_delay);
	av_log(ctx, AV_LOG_INFO,
		"bitrate max/min/avg: %d/%d/%d buffer size: %d vbv_delay: %lld",
		cpb->max_bitrate, cpb->min_bitrate, cpb->avg_bitrate,
		cpb->buffer_size,
		cpb->vbv_delay);

	return retstr;
}

static std::string dump_mastering_display_metadata(void *ctx, AVPacketSideData* sd) {
	std::string retstr = "";
	AVMasteringDisplayMetadata* metadata = (AVMasteringDisplayMetadata*)sd->data;
	retstr += AssemblyBuffer("Mastering Display Metadata, "
		"has_primaries:%d has_luminance:%d "
		"r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
		"min_luminance=%f, max_luminance=%f",
		metadata->has_primaries, metadata->has_luminance,
		av_q2d(metadata->display_primaries[0][0]),
		av_q2d(metadata->display_primaries[0][1]),
		av_q2d(metadata->display_primaries[1][0]),
		av_q2d(metadata->display_primaries[1][1]),
		av_q2d(metadata->display_primaries[2][0]),
		av_q2d(metadata->display_primaries[2][1]),
		av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1]),
		av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance));

	av_log(ctx, AV_LOG_INFO, "Mastering Display Metadata, "
		"has_primaries:%d has_luminance:%d "
		"r(%5.4f,%5.4f) g(%5.4f,%5.4f) b(%5.4f %5.4f) wp(%5.4f, %5.4f) "
		"min_luminance=%f, max_luminance=%f",
		metadata->has_primaries, metadata->has_luminance,
		av_q2d(metadata->display_primaries[0][0]),
		av_q2d(metadata->display_primaries[0][1]),
		av_q2d(metadata->display_primaries[1][0]),
		av_q2d(metadata->display_primaries[1][1]),
		av_q2d(metadata->display_primaries[2][0]),
		av_q2d(metadata->display_primaries[2][1]),
		av_q2d(metadata->white_point[0]), av_q2d(metadata->white_point[1]),
		av_q2d(metadata->min_luminance), av_q2d(metadata->max_luminance));
	return retstr;
}


static std::string dump_spherical(void *ctx, AVCodecParameters *par, AVPacketSideData *sd)
{
	std::string retstr = "";
	AVSphericalMapping *spherical = (AVSphericalMapping *)sd->data;
	double yaw, pitch, roll;

	if (sd->size < sizeof(*spherical)) {
		retstr += AssemblyBuffer("invalid data");
		av_log(ctx, AV_LOG_INFO, "invalid data");
		return retstr;
	}
	retstr += AssemblyBuffer("%s ", av_spherical_projection_name(spherical->projection));
	av_log(ctx, AV_LOG_INFO, "%s ", av_spherical_projection_name(spherical->projection));

	yaw = ((double)spherical->yaw) / (1 << 16);
	pitch = ((double)spherical->pitch) / (1 << 16);
	roll = ((double)spherical->roll) / (1 << 16);
	retstr += AssemblyBuffer("(%f/%f/%f) ", yaw, pitch, roll);
	av_log(ctx, AV_LOG_INFO, "(%f/%f/%f) ", yaw, pitch, roll);

	if (spherical->projection == AV_SPHERICAL_EQUIRECTANGULAR_TILE) {
		size_t l, t, r, b;
		av_spherical_tile_bounds(spherical, par->width, par->height,
			&l, &t, &r, &b);
		retstr += AssemblyBuffer("[%zu, %zu, %zu, %zu] ",
			l, t, r, b);
		av_log(ctx, AV_LOG_INFO,
			"[%zu, %zu, %zu, %zu] ",
			l, t, r, b);
	}
	else if (spherical->projection == AV_SPHERICAL_CUBEMAP) {
		retstr += AssemblyBuffer("[pad %u] ", spherical->padding);
		av_log(ctx, AV_LOG_INFO, "[pad %u] ", spherical->padding);
	}
	return retstr;
}
static std::string dump_content_light_metadata(void *ctx, AVPacketSideData* sd)
{
	std::string retstr = "";
	AVContentLightMetadata* metadata = (AVContentLightMetadata*)sd->data;
	retstr += AssemblyBuffer("Content Light Level Metadata, "
		"MaxCLL=%d, MaxFALL=%d",
		metadata->MaxCLL, metadata->MaxFALL);
	av_log(ctx, AV_LOG_INFO, "Content Light Level Metadata, "
		"MaxCLL=%d, MaxFALL=%d",
		metadata->MaxCLL, metadata->MaxFALL);

	return retstr;
}
// fixed point to double
#define CONV_FP(x) ((double) (x)) / (1 << 16)
double av_display_rotation_get(const int32_t matrix[9])
{
	double rotation, scale[2];

	scale[0] = hypot(CONV_FP(matrix[0]), CONV_FP(matrix[3]));
	scale[1] = hypot(CONV_FP(matrix[1]), CONV_FP(matrix[4]));

	if (scale[0] == 0.0 || scale[1] == 0.0)
		return NAN;

	rotation = atan2(CONV_FP(matrix[1]) / scale[1],
		CONV_FP(matrix[0]) / scale[0]) * 180 / M_PI;

	return -rotation;
}
static std::string dump_sidedata(void *ctx, AVStream *st, const char *indent)
{
	int i;
	std::string retstr = "";

	if (st->nb_side_data)
	{
		retstr += AssemblyBuffer("%sSide data:\n", indent);
		av_log(ctx, AV_LOG_INFO, "%sSide data:\n", indent);
	}

	for (i = 0; i < st->nb_side_data; i++) {
		AVPacketSideData sd = st->side_data[i];
		retstr += AssemblyBuffer("%s  ");
		av_log(ctx, AV_LOG_INFO, "%s  ", indent);

		switch (sd.type) {
		case AV_PKT_DATA_PALETTE:
			retstr += AssemblyBuffer("palette");
			av_log(ctx, AV_LOG_INFO, "palette");
			break;
		case AV_PKT_DATA_NEW_EXTRADATA:
			retstr += AssemblyBuffer("new extradata");
			av_log(ctx, AV_LOG_INFO, "new extradata");
			break;
		case AV_PKT_DATA_PARAM_CHANGE:
			retstr += AssemblyBuffer("paramchange: ");
			av_log(ctx, AV_LOG_INFO, "paramchange: ");
			retstr += dump_paramchange(ctx, &sd);
			break;
		case AV_PKT_DATA_H263_MB_INFO:
			retstr += AssemblyBuffer("H.263 macroblock info");
			av_log(ctx, AV_LOG_INFO, "H.263 macroblock info");
			break;
		case AV_PKT_DATA_REPLAYGAIN:
			retstr += AssemblyBuffer("replaygain: ");
			av_log(ctx, AV_LOG_INFO, "replaygain: ");
			retstr += dump_replaygain(ctx, &sd);
			break;
		case AV_PKT_DATA_DISPLAYMATRIX:
			retstr += AssemblyBuffer("displaymatrix: rotation of %.2f degrees",
				av_display_rotation_get((int32_t *)sd.data));
			av_log(ctx, AV_LOG_INFO, "displaymatrix: rotation of %.2f degrees",
				av_display_rotation_get((int32_t *)sd.data));
			break;
		case AV_PKT_DATA_STEREO3D:
			retstr += AssemblyBuffer("stereo3d: ");
			av_log(ctx, AV_LOG_INFO, "stereo3d: ");
			retstr += dump_stereo3d(ctx, &sd);
			break;
		case AV_PKT_DATA_AUDIO_SERVICE_TYPE:
			retstr += AssemblyBuffer("audio service type: ");
			av_log(ctx, AV_LOG_INFO, "audio service type: ");
			retstr += dump_audioservicetype(ctx, &sd);
			break;
		case AV_PKT_DATA_QUALITY_STATS:
			retstr += AssemblyBuffer("quality factor: %d, pict_type: %c",
				AV_RL32(sd.data), av_get_picture_type_char((AVPictureType)sd.data[4]));
			av_log(ctx, AV_LOG_INFO, "quality factor: %d, pict_type: %c",
				AV_RL32(sd.data), av_get_picture_type_char((AVPictureType)sd.data[4]));
			break;
		case AV_PKT_DATA_CPB_PROPERTIES:
			retstr += AssemblyBuffer("cpb: ");
			av_log(ctx, AV_LOG_INFO, "cpb: ");
			retstr += dump_cpb(ctx, &sd);
			break;
		case AV_PKT_DATA_MASTERING_DISPLAY_METADATA:
			retstr += dump_mastering_display_metadata(ctx, &sd);
			break;
		case AV_PKT_DATA_SPHERICAL:
			retstr += AssemblyBuffer("spherical: ");
			av_log(ctx, AV_LOG_INFO, "spherical: ");
			retstr += dump_spherical(ctx, st->codecpar, &sd);
			break;
		case AV_PKT_DATA_CONTENT_LIGHT_LEVEL:
			retstr += dump_content_light_metadata(ctx, &sd);
			break;
		default:
			retstr += AssemblyBuffer("unknown side data type %d (%d bytes)", sd.type, sd.size);
			av_log(ctx, AV_LOG_INFO,
				"unknown side data type %d (%d bytes)", sd.type, sd.size);
			break;
		}

		retstr += AssemblyBuffer("\n");
		av_log(ctx, AV_LOG_INFO, "\n");
	}
	return retstr;
}

/* "user interface" functions */
static std::string dump_stream_format(AVFormatContext *ic, int i,
	int index, int is_output)
{
	std::string retstr = "";
	char buf[256];
	int flags = (is_output ? ic->oformat->flags : ic->iformat->flags);
	AVStream *st = ic->streams[i];
	AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL, 0);
	char *separator = (char*)ic->dump_separator;
	AVCodecContext *avctx;
	int ret;

	avctx = avcodec_alloc_context3(NULL);
	if (!avctx)
		return retstr;

	ret = avcodec_parameters_to_context(avctx, st->codecpar);
	if (ret < 0) {
		avcodec_free_context(&avctx);
		return retstr;
	}

	// Fields which are missing from AVCodecParameters need to be taken from the AVCodecContext
	avctx->properties = st->codec->properties;
	avctx->codec = st->codec->codec;
	avctx->qmin = st->codec->qmin;
	avctx->qmax = st->codec->qmax;
	avctx->coded_width = st->codec->coded_width;
	avctx->coded_height = st->codec->coded_height;

	if (separator)
		av_opt_set(avctx, "dump_separator", separator, 0);
	avcodec_string(buf, sizeof(buf), avctx, is_output);
	avcodec_free_context(&avctx);

	retstr += AssemblyBuffer("    Stream #%d:%d", index, i);
	av_log(NULL, AV_LOG_INFO, "    Stream #%d:%d", index, i);

	/* the pid is an important information, so we display it */
	/* XXX: add a generic system */
	if (flags & AVFMT_SHOW_IDS)
	{
		retstr += AssemblyBuffer("[0x%x]", st->id);
		av_log(NULL, AV_LOG_INFO, "[0x%x]", st->id);
	}
	if (lang)
	{
		retstr += AssemblyBuffer("(%s)", lang->value);
		av_log(NULL, AV_LOG_INFO, "(%s)", lang->value);
	}
	//retstr += AssemblyBuffer(", %d, %d/%d", st->codec_info_nb_frames,
	//	st->time_base.num, st->time_base.den);
	av_log(NULL, AV_LOG_DEBUG, ", %d, %d/%d", st->codec_info_nb_frames,
		st->time_base.num, st->time_base.den);
	retstr += AssemblyBuffer(": %s", buf);
	av_log(NULL, AV_LOG_INFO, ": %s", buf);

	if (st->sample_aspect_ratio.num &&
		av_cmp_q(st->sample_aspect_ratio, st->codecpar->sample_aspect_ratio)) {
		AVRational display_aspect_ratio;
		av_reduce(&display_aspect_ratio.num, &display_aspect_ratio.den,
			st->codecpar->width  * (int64_t)st->sample_aspect_ratio.num,
			st->codecpar->height * (int64_t)st->sample_aspect_ratio.den,
			1024 * 1024);
		retstr += AssemblyBuffer(", SAR %d:%d DAR %d:%d",
			st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
			display_aspect_ratio.num, display_aspect_ratio.den);
		av_log(NULL, AV_LOG_INFO, ", SAR %d:%d DAR %d:%d",
			st->sample_aspect_ratio.num, st->sample_aspect_ratio.den,
			display_aspect_ratio.num, display_aspect_ratio.den);
	}

	if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
		int fps = st->avg_frame_rate.den && st->avg_frame_rate.num;
		int tbr = st->r_frame_rate.den && st->r_frame_rate.num;
		int tbn = st->time_base.den && st->time_base.num;
		int tbc = st->codec->time_base.den && st->codec->time_base.num;

		if (fps || tbr || tbn || tbc)
		{
			retstr += AssemblyBuffer("%s", separator);
			av_log(NULL, AV_LOG_INFO, "%s", separator);
		}
			

		if (fps)
			retstr += print_fps(av_q2d(st->avg_frame_rate), tbr || tbn || tbc ? "fps, " : "fps");
		if (tbr)
			retstr += print_fps(av_q2d(st->r_frame_rate), tbn || tbc ? "tbr, " : "tbr");
		if (tbn)
			retstr += print_fps(1 / av_q2d(st->time_base), tbc ? "tbn, " : "tbn");
		if (tbc)
			retstr += print_fps(1 / av_q2d(st->codec->time_base), "tbc");
	}

	if (st->disposition & AV_DISPOSITION_DEFAULT)
	{
		retstr += AssemblyBuffer(" (default)");
		av_log(NULL, AV_LOG_INFO, " (default)");
	}
	if (st->disposition & AV_DISPOSITION_DUB)
	{
		retstr += AssemblyBuffer(" (dub)");
		av_log(NULL, AV_LOG_INFO, " (dub)");
	}
	if (st->disposition & AV_DISPOSITION_ORIGINAL)
	{
		retstr += AssemblyBuffer(" (original)");
		av_log(NULL, AV_LOG_INFO, " (original)");
	}
	if (st->disposition & AV_DISPOSITION_COMMENT)
	{
		retstr += AssemblyBuffer(" (comment)");
		av_log(NULL, AV_LOG_INFO, " (comment)");
	}
	if (st->disposition & AV_DISPOSITION_LYRICS)
	{
		retstr += AssemblyBuffer(" (lyrics)");
		av_log(NULL, AV_LOG_INFO, " (lyrics)");
	}
	if (st->disposition & AV_DISPOSITION_KARAOKE)
	{
		retstr += AssemblyBuffer(" (karaoke)");
		av_log(NULL, AV_LOG_INFO, " (karaoke)");
	}
	if (st->disposition & AV_DISPOSITION_FORCED)
	{
		retstr += AssemblyBuffer(" (forced)");
		av_log(NULL, AV_LOG_INFO, " (forced)");
	}
	if (st->disposition & AV_DISPOSITION_HEARING_IMPAIRED)
	{
		retstr += AssemblyBuffer(" (hearing impaired)");
		av_log(NULL, AV_LOG_INFO, " (hearing impaired)");
	}
	if (st->disposition & AV_DISPOSITION_VISUAL_IMPAIRED)
	{
		retstr += AssemblyBuffer(" (visual impaired)");
		av_log(NULL, AV_LOG_INFO, " (visual impaired)");
	}
	if (st->disposition & AV_DISPOSITION_CLEAN_EFFECTS)
	{
		retstr += AssemblyBuffer(" (clean effects)");
		av_log(NULL, AV_LOG_INFO, " (clean effects)");
	}
	if (st->disposition & AV_DISPOSITION_ATTACHED_PIC)
	{
		retstr += AssemblyBuffer(" (attached pic)");
		av_log(NULL, AV_LOG_INFO, " (attached pic)");
	}
	if (st->disposition & AV_DISPOSITION_TIMED_THUMBNAILS)
	{
		retstr += AssemblyBuffer(" (timed thumbnails)");
		av_log(NULL, AV_LOG_INFO, " (timed thumbnails)");
	}
	if (st->disposition & AV_DISPOSITION_CAPTIONS)
	{
		retstr += AssemblyBuffer(" (captions)");
		av_log(NULL, AV_LOG_INFO, " (captions)");
	}
	if (st->disposition & AV_DISPOSITION_DESCRIPTIONS)
	{
		retstr += AssemblyBuffer(" (descriptions)");
		av_log(NULL, AV_LOG_INFO, " (descriptions)");
	}
	if (st->disposition & AV_DISPOSITION_METADATA)
	{
		retstr += AssemblyBuffer(" (metadata)");
		av_log(NULL, AV_LOG_INFO, " (metadata)");
	}
	if (st->disposition & AV_DISPOSITION_DEPENDENT)
	{
		retstr += AssemblyBuffer(" (dependent)");
		av_log(NULL, AV_LOG_INFO, " (dependent)");
	}
	if (st->disposition & AV_DISPOSITION_STILL_IMAGE)
	{
		retstr += AssemblyBuffer(" (still image)");
		av_log(NULL, AV_LOG_INFO, " (still image)");
	}
	retstr += AssemblyBuffer("\n");
	av_log(NULL, AV_LOG_INFO, "\n");

	retstr += dump_metadata(NULL, st->metadata, "    ");

	retstr += dump_sidedata(NULL, st, "    ");
	return retstr;
}


std::string AV_Dump_Format(AVFormatContext *ic, int index, const char *url, int is_output)
{
	std::string retstr = "";
	int i;
	uint8_t *printed = ic->nb_streams ? (uint8_t *)av_mallocz(ic->nb_streams) : NULL;
	if (ic->nb_streams && !printed)
		return retstr;

	retstr += AssemblyBuffer("%s #%d, %s, %s '%s':\n",
		is_output ? "Output" : "Input",
		index,
		is_output ? ic->oformat->name : ic->iformat->name,
		is_output ? "to" : "from", url);
	av_log(NULL, AV_LOG_INFO, "%s #%d, %s, %s '%s':\n",
		is_output ? "Output" : "Input",
		index,
		is_output ? ic->oformat->name : ic->iformat->name,
		is_output ? "to" : "from", url);
	retstr += dump_metadata(NULL, ic->metadata, "  ");

	if (!is_output) {
		retstr += AssemblyBuffer("  Duration: ");
		av_log(NULL, AV_LOG_INFO, "  Duration: ");
		if (ic->duration != AV_NOPTS_VALUE) {
			int hours, mins, secs, us;
			int64_t duration = ic->duration + (ic->duration <= INT64_MAX - 5000 ? 5000 : 0);
			secs = duration / AV_TIME_BASE;
			us = duration % AV_TIME_BASE;
			mins = secs / 60;
			secs %= 60;
			hours = mins / 60;
			mins %= 60;
			retstr += AssemblyBuffer("%02d:%02d:%02d.%02d", hours, mins, secs,
				(100 * us) / AV_TIME_BASE);
			av_log(NULL, AV_LOG_INFO, "%02d:%02d:%02d.%02d", hours, mins, secs,
				(100 * us) / AV_TIME_BASE);
		}
		else {
			retstr += AssemblyBuffer("N/A");
			av_log(NULL, AV_LOG_INFO, "N/A");
		}
		if (ic->start_time != AV_NOPTS_VALUE) {
			int secs, us;
			retstr += AssemblyBuffer(", start: ");
			av_log(NULL, AV_LOG_INFO, ", start: ");
			secs = llabs(ic->start_time / AV_TIME_BASE);
			us = llabs(ic->start_time % AV_TIME_BASE);
			retstr += AssemblyBuffer("%s%d.%06d",
				ic->start_time >= 0 ? "" : "-",
				secs,
				(int)av_rescale(us, 1000000, AV_TIME_BASE));
			av_log(NULL, AV_LOG_INFO, "%s%d.%06d",
				ic->start_time >= 0 ? "" : "-",
				secs,
				(int)av_rescale(us, 1000000, AV_TIME_BASE));
		}
		retstr += AssemblyBuffer(", bitrate: ");
		av_log(NULL, AV_LOG_INFO, ", bitrate: ");
		if (ic->bit_rate)
		{
			retstr += AssemblyBuffer("%lld kb/s", ic->bit_rate / 1000);
			av_log(NULL, AV_LOG_INFO, "%lld kb/s", ic->bit_rate / 1000);
		}
		else
		{
			retstr += AssemblyBuffer("N/A");
			av_log(NULL, AV_LOG_INFO, "N/A");
		}
		retstr += AssemblyBuffer("\n");
		av_log(NULL, AV_LOG_INFO, "\n");
	}

	for (i = 0; i < ic->nb_chapters; i++) {
		AVChapter *ch = ic->chapters[i];
		retstr += AssemblyBuffer("    Chapter #%d:%d: ", index, i);
		av_log(NULL, AV_LOG_INFO, "    Chapter #%d:%d: ", index, i);
		retstr += AssemblyBuffer("start %f, ", ch->start * av_q2d(ch->time_base));
		av_log(NULL, AV_LOG_INFO,
			"start %f, ", ch->start * av_q2d(ch->time_base));
		retstr += AssemblyBuffer("end %f\n", ch->end * av_q2d(ch->time_base));
		av_log(NULL, AV_LOG_INFO,
			"end %f\n", ch->end * av_q2d(ch->time_base));

		retstr += dump_metadata(NULL, ch->metadata, "    ");
	}

	if (ic->nb_programs) {
		int j, k, total = 0;
		for (j = 0; j < ic->nb_programs; j++) {
			AVDictionaryEntry *name = av_dict_get(ic->programs[j]->metadata,
				"name", NULL, 0);
			retstr += AssemblyBuffer("  Program %d %s\n", ic->programs[j]->id,
				name ? name->value : "");
			av_log(NULL, AV_LOG_INFO, "  Program %d %s\n", ic->programs[j]->id,
				name ? name->value : "");
			retstr += dump_metadata(NULL, ic->programs[j]->metadata, "    ");
			for (k = 0; k < ic->programs[j]->nb_stream_indexes; k++) {
				retstr += dump_stream_format(ic, ic->programs[j]->stream_index[k],
					index, is_output);
				printed[ic->programs[j]->stream_index[k]] = 1;
			}
			total += ic->programs[j]->nb_stream_indexes;
		}
		if (total < ic->nb_streams)
		{
			retstr += AssemblyBuffer("  No Program\n");
			av_log(NULL, AV_LOG_INFO, "  No Program\n");
		}
	}

	for (i = 0; i < ic->nb_streams; i++)
		if (!printed[i])
			retstr += dump_stream_format(ic, i, index, is_output);

	av_free(printed);

	printf("=================================\n");
	printf("%s\n", retstr.c_str());
	printf("=================================\n");
	return retstr;
}